﻿using System;
using System.Collections.Generic;
using System.Linq;
using Assist;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using YArchitech.Revit;
using YArchitech.Revit.RevitExtension;
using YJKArchMethodLibrary.Utils;
using YJKRevitCode.RevitDimensionUtility;
using YJKRGeometry;

namespace YJKTextDimension.DoorWindowDim
{
	public class DoorWindowDimensionManager
	{
		public DoorWindowDimensionManager(ExternalCommandData cmdData)
		{
			this.m_doc = cmdData.Application.ActiveUIDocument.Document;
			this.m_view = this.m_doc.ActiveView;
		}

		public List<Level> GetAllLevels()
		{
			List<Level> list = new List<Level>();
			FilteredElementCollector filteredElementCollector = new FilteredElementCollector(this.m_doc);
			List<ElementId> list2 = filteredElementCollector.OfClass(typeof(Level)).ToElementIds().ToList<ElementId>();
			foreach (ElementId elementId in list2)
			{
				list.Add(this.m_doc.GetElement(elementId) as Level);
			}
			list.Sort(new LevelComparer());
			return list;
		}

		public void CreateDimension(List<List<Element>> lstDimensionGroups)
		{
			if (lstDimensionGroups == null)
			{
				return;
			}
			Transaction transaction = new Transaction(this.m_doc, "Create Dimension");
			try
			{
				transaction.Start();
				RevitDimensionUtiltyMethod.GetDimensionType(this.m_doc, DimensionTypeEnum.ScaleDimension, null);
				new Dictionary<double, Reference>();
				foreach (List<Element> lstGroups in lstDimensionGroups)
				{
					new ReferenceArray();
					Dictionary<double, Reference> dictionary = new Dictionary<double, Reference>();
					Line line = null;
					Line dimensionLine = this.GetDimensionLine(lstGroups, line);
					dictionary = this.GetReferences(lstGroups, dimensionLine, ref line);
					List<double> list = dictionary.Keys.ToList<double>();
					list.Sort();
					list = list.Distinct<double>().ToList<double>();
					Dimension dimension = DoorWindowDimensionManager.CreateDimByElevatorAndLine(this.m_doc, list, dimensionLine, this.m_view.RightDirection);
					if (dimension != null)
					{
						RevitDimensionExtension.ResetDimTextPos(dimension);
					}
				}
				transaction.Commit();
			}
			catch (Exception)
			{
				if ((TransactionStatus)1 == transaction.GetStatus())
				{
					transaction.RollBack();
				}
			}
		}

		public static Dimension CreateDimByElevatorAndLine(Autodesk.Revit.DB.Document doc, List<double> lstdElev, Line BaseLine, XYZ BaseNoraml)
		{
			XYZ origin = BaseLine.Origin;
			double num = Common.MMToFeet(400.0);
			ReferenceArray referenceArray = new ReferenceArray();
			foreach (double num2 in lstdElev)
			{
				double num3 = num2;
				XYZ xyz = new XYZ(origin.X, origin.Y, num3) + num / 2.0 * BaseNoraml.Negate();
				XYZ xyz2 = xyz + num * BaseNoraml.Negate();
				Line line = Line.CreateBound(xyz, xyz2);
				DetailCurve detailCurve = doc.Create.NewDetailCurve(doc.ActiveView, line);
				doc.Regenerate();
				Reference reference = new Reference(detailCurve);
				referenceArray.Append(reference);
			}
			ElementId dimSymbol = DoorWindowDimensionManager.GetDimSymbol(doc);
			return doc.Create.NewDimension(doc.ActiveView, BaseLine, referenceArray, doc.GetElement(dimSymbol) as DimensionType);
		}

		public static ElementId GetDimSymbol(Autodesk.Revit.DB.Document doc)
		{
			DimensionType dimensionType = RevitDimensionUtiltyMethod.GetDimensionType(doc, DimensionTypeEnum.ScaleDimension, null);
			if (dimensionType != null)
			{
				return dimensionType.Id;
			}
			return ElementId.InvalidElementId;
		}

		public List<Face> GetDimensionFaces(Wall wall, Element elem)
		{
			List<Face> list = new List<Face>();
			GeometryElement geometryElement = wall.get_Geometry(new Options
			{
				ComputeReferences = true
			});
			XYZ xyz = new XYZ(0.0, 0.0, 1.0);
			XYZ xyz2 = new XYZ(0.0, 0.0, -1.0);
			foreach (GeometryObject geometryObject in geometryElement)
			{
				if (geometryObject is Solid)
				{
					Solid solid = geometryObject as Solid;
					if (!(null == solid))
					{
						foreach (object obj in solid.Faces)
						{
							Face face = (Face)obj;
							List<ElementId> list2 = wall.GetGeneratingElementIds(face).ToList<ElementId>();
							if (list2.Contains(elem.Id))
							{
								PlanarFace planarFace = face as PlanarFace;
								if (planarFace.FaceNormal.Normalize().IsAlmostEqualTo(xyz) || planarFace.FaceNormal.Normalize().IsAlmostEqualTo(xyz2))
								{
									list.Add(face);
								}
							}
						}
					}
				}
			}
			return list;
		}

		public Dictionary<double, Face> GetDimensionFaces1(Wall wall, Element elem)
		{
			Dictionary<double, Face> dictionary = new Dictionary<double, Face>();
			GeometryElement geometryElement = wall.get_Geometry(new Options
			{
				ComputeReferences = true
			});
			XYZ basisZ = XYZ.BasisZ;
			XYZ xyz = basisZ.Negate();
			foreach (GeometryObject geometryObject in geometryElement)
			{
				if (geometryObject is Solid)
				{
					Solid solid = geometryObject as Solid;
					if (!(null == solid))
					{
						foreach (object obj in solid.Faces)
						{
							Face face = (Face)obj;
							List<ElementId> list = wall.GetGeneratingElementIds(face).ToList<ElementId>();
							if (list.Contains(elem.Id))
							{
								PlanarFace planarFace = face as PlanarFace;
								if (planarFace.FaceNormal.Normalize().IsAlmostEqualTo(basisZ) || planarFace.FaceNormal.Normalize().IsAlmostEqualTo(xyz))
								{
									dictionary.Add(Math.Round(planarFace.Origin.Z, 5), face);
								}
							}
						}
					}
				}
			}
			return dictionary;
		}

		public List<FamilyInstance> GetInstancesOnWall(List<Wall> lstOutWalls)
		{
			if (lstOutWalls == null || 0 >= lstOutWalls.Count)
			{
				return null;
			}
			List<FamilyInstance> list = new List<FamilyInstance>();
			List<int> list2 = new List<int>();
			Options options = new Options();
			options.ComputeReferences = true;
			foreach (Wall wall in lstOutWalls)
			{
				Document document = wall.Document;
				if (wall != null)
				{
					GeometryElement geometryElement = wall.get_Geometry(options);
					foreach (GeometryObject geometryObject in geometryElement)
					{
						if (geometryObject is Solid)
						{
							Solid solid = geometryObject as Solid;
							if (!(null == solid))
							{
								foreach (object obj in solid.Faces)
								{
									Face face = (Face)obj;
									List<ElementId> list3 = wall.GetGeneratingElementIds(face).ToList<ElementId>();
									foreach (ElementId elementId in list3)
									{
										Element element = document.GetElement(elementId);
										if (typeof(FamilyInstance) == element.GetType())
										{
											FamilyInstance familyInstance = element as FamilyInstance;
											if (familyInstance != null && (familyInstance.Category.Id.IntegerValue == -2000014 || familyInstance.Category.Id.IntegerValue == -2000023) && !list2.Contains(elementId.IntegerValue))
											{
												list2.Add(elementId.IntegerValue);
												list.Add(familyInstance);
											}
										}
									}
								}
							}
						}
					}
				}
			}
			return list;
		}

		public Line OneFaceSituation(Element elem, Face faceExisted)
		{
			double z = (faceExisted as PlanarFace).Origin.Z;
			double instanceHeight = this.GetInstanceHeight(elem);
			this.GetInstanceWidth(elem);
			XYZ instanceLocationPoint = this.GetInstanceLocationPoint(elem);
			XYZ origin = (faceExisted as PlanarFace).Origin;
			double num;
			if (1E-06 < z - instanceLocationPoint.Z)
			{
				num = origin.Z - instanceHeight;
			}
			else
			{
				num = origin.Z + instanceHeight;
			}
			XYZ xyz = this.m_view.RightDirection * 0.0032808398950131233;
			XYZ xyz2 = new XYZ(origin.X, origin.Y, num);
			XYZ xyz3 = new XYZ(origin.X, origin.Y, num) - xyz;
			return Line.CreateBound(xyz2, xyz3);
		}

		public double GetInstanceWidth(Element elem)
		{
			FamilyInstance familyInstance = elem as FamilyInstance;
			FamilySymbol symbol = familyInstance.Symbol;
			double result;
			if (symbol.Category.ToString().Contains("OST_Windows"))
			{
				result = symbol.GetParameter(BuiltInParameter.CASEWORK_WIDTH).AsDouble();
			}
			else
			{
				result = symbol.GetParameter(BuiltInParameter.CASEWORK_WIDTH).AsDouble();
			}
			return result;
		}

		public double GetInstanceHeight(Element elem)
		{
			FamilyInstance familyInstance = elem as FamilyInstance;
			FamilySymbol symbol = familyInstance.Symbol;
			double result;
			if (symbol.Category.ToString().Contains("OST_Windows"))
			{
				result = symbol.GetParameter(BuiltInParameter.FAMILY_HEIGHT_PARAM).AsDouble();
			}
			else
			{
				result = symbol.GetParameter(BuiltInParameter.FAMILY_HEIGHT_PARAM).AsDouble();
			}
			return result;
		}

		public XYZ GetInstanceLocationPoint(Element elem)
		{
			FamilyInstance familyInstance = elem as FamilyInstance;
			FamilySymbol symbol = familyInstance.Symbol;
			return (familyInstance.Location as LocationPoint).Point;
		}

		private Dictionary<double, Reference> GetReferences(List<Element> lstGroups, Line inputline, ref Line line)
		{
			Dictionary<double, Reference> dictionary = new Dictionary<double, Reference>();
			Plane plane = RevitVersionFuncs.CreatePlanByOriginAndBasis(this.m_view.Origin, this.m_view.RightDirection, this.m_view.UpDirection);
			Transform.CreateReflection(plane);
			Transform.CreateRotationAtPoint(this.m_view.ViewDirection, Math.PI*.5, this.m_view.Origin);
			foreach (Element element in lstGroups)
			{
				line = null;
				if (typeof(FamilyInstance) == element.GetType())
				{
					Dictionary<double, Face> dimensionFaces = this.GetDimensionFaces1((element as FamilyInstance).Host as Wall, element);
					if (2 == dimensionFaces.Count)
					{
						using (Dictionary<double, Face>.Enumerator enumerator2 = dimensionFaces.GetEnumerator())
						{
							while (enumerator2.MoveNext())
							{
								KeyValuePair<double, Face> keyValuePair = enumerator2.Current;
								if (!dictionary.ContainsKey(keyValuePair.Key))
								{
									PlanarFace planarFace = keyValuePair.Value as PlanarFace;
									Line line2 = Line.CreateBound(planarFace.Origin, planarFace.Origin.Add(this.m_view.RightDirection * 1.0 / 304.8));
									DetailCurve detailCurve = this.m_doc.Create.NewDetailCurve(this.m_view, line2);
									dictionary.Add(keyValuePair.Key, detailCurve.GeometryCurve.Reference);
								}
							}
							continue;
						}
					}
					line = this.OneFaceSituation(element, dimensionFaces.ElementAt(0).Value);
					if (null != line)
					{
						DetailCurve detailCurve2 = this.m_doc.Create.NewDetailCurve(this.m_view, line);
						if (!dictionary.ContainsKey(dimensionFaces.ElementAt(0).Key))
						{
							PlanarFace planarFace2 = dimensionFaces.ElementAt(0).Value as PlanarFace;
							Line line3 = Line.CreateBound(planarFace2.Origin, planarFace2.Origin.Add(this.m_view.RightDirection * 1.0 / 304.8));
							DetailCurve detailCurve3 = this.m_doc.Create.NewDetailCurve(this.m_view, line3);
							dictionary.Add(dimensionFaces.ElementAt(0).Key, detailCurve3.GeometryCurve.Reference);
						}
						if (!dictionary.ContainsKey(Math.Round(line.GetEndPoint(0).Z, 5)))
						{
							dictionary.Add(Math.Round(line.GetEndPoint(0).Z, 5), detailCurve2.GeometryCurve.Reference);
						}
					}
				}
				else
				{
					Level level = element as Level;
					if (level != null && !dictionary.ContainsKey(Math.Round(level.Elevation, 5)))
					{
						dictionary.Add(Math.Round(level.Elevation, 5), new Reference(level));
					}
				}
			}
			return dictionary;
		}

		private bool IsTwoLineCollineation(Curve oneCurve, Curve twoCurve)
		{
			bool flag = YJKRGeometry.Geometry.IsParallel(oneCurve.GetEndPoint(1) - oneCurve.GetEndPoint(0), twoCurve.GetEndPoint(1) - twoCurve.GetEndPoint(0));
			bool flag2 = YJKRGeometry.Geometry.Is_Point_OnSegment(oneCurve.GetEndPoint(1), oneCurve.GetEndPoint(0), twoCurve.GetEndPoint(0), 1E-09);
			bool flag3 = YJKRGeometry.Geometry.Is_Point_OnSegment(oneCurve.GetEndPoint(1), oneCurve.GetEndPoint(0), twoCurve.GetEndPoint(1), 1E-09);
			return flag && flag2 && flag3;
		}

		private bool ReferenceArrayContains(Dictionary<Curve, Reference> dicDimRefs, Curve inputCurve)
		{
			foreach (KeyValuePair<Curve, Reference> keyValuePair in dicDimRefs)
			{
				if (this.IsTwoLineCollineation(inputCurve, keyValuePair.Key))
				{
					return true;
				}
			}
			return false;
		}

		private XYZ DimensionInsertPoint(List<Element> lstElements)
		{
			Element rightElement = this.GetRightElement(lstElements);
			XYZ instanceLocationPoint = this.GetInstanceLocationPoint(rightElement);
			double instanceWidth = this.GetInstanceWidth(rightElement);
			double instanceHeight = this.GetInstanceHeight(rightElement);
			XYZ xyz = this.m_view.RightDirection * (instanceWidth / 2.0);
            //instanceLocationPoint - xyz;
			XYZ xyz2 = instanceLocationPoint + xyz;
			xyz = XYZ.BasisZ * instanceHeight;
			XYZ xyz3 = xyz2 + xyz;
			xyz = this.m_view.RightDirection * this.m_dOffset;
			return xyz3 + xyz;
		}

		private Element GetRightElement(List<Element> lstElements)
		{
			Element result = null;
			if (this.m_view.ViewDirection.IsAlmostEqualTo(new XYZ(1.0, 0.0, 0.0)))
			{
				result = this.GetElementByOriention(false, true, lstElements);
			}
			else if (this.m_view.ViewDirection.IsAlmostEqualTo(new XYZ(-1.0, 0.0, 0.0)))
			{
				result = this.GetElementByOriention(false, false, lstElements);
			}
			else if (this.m_view.ViewDirection.IsAlmostEqualTo(new XYZ(0.0, -1.0, 0.0)))
			{
				result = this.GetElementByOriention(true, true, lstElements);
			}
			else if (this.m_view.ViewDirection.IsAlmostEqualTo(new XYZ(0.0, 1.0, 0.0)))
			{
				result = this.GetElementByOriention(true, false, lstElements);
			}
			return result;
		}

		private Element GetElementByOriention(bool bAxis, bool bRelation, List<Element> lstElements)
		{
			Element element = lstElements.ElementAt(0);
			double valueByAxis = this.GetValueByAxis(element, bAxis);
			for (int i = 1; i < lstElements.Count; i++)
			{
				Element element2 = lstElements.ElementAt(i);
				if (typeof(FamilyInstance) == element2.GetType())
				{
					double valueByAxis2 = this.GetValueByAxis(element2, bAxis);
					this.ExchangeElementByRelation(bRelation, ref element, element2, ref valueByAxis, valueByAxis2);
				}
			}
			return element;
		}

		private double GetValueByAxis(Element elem, bool bAxis)
		{
			double result;
			if (bAxis)
			{
				result = this.GetInstanceLocationPoint(elem).X;
			}
			else
			{
				result = this.GetInstanceLocationPoint(elem).Y;
			}
			return result;
		}

		private void ExchangeElementByRelation(bool bRelation, ref Element elemReturn, Element elemCompare, ref double dValueReturn, double dValueCompare)
		{
			if (bRelation)
			{
				if (dValueReturn < dValueCompare)
				{
					dValueReturn = dValueCompare;
					elemReturn = elemCompare;
					return;
				}
			}
			else if (dValueReturn > dValueCompare)
			{
				dValueReturn = dValueCompare;
				elemReturn = elemCompare;
			}
		}

		private DimensionParameter SetDimensionParameter()
		{
			return new DimensionParameter
			{
				m_nLeadLine = 1,
				m_idLead = DimensionTypeManager.GetDimLeaderHeadTypeId("对角线 1.0mm"),
				m_nLeadWidth = 3,
				m_nDimensionLineControlPoint = 1,
				m_dDimensionLineLength = Common.MMToFeet(8.0),
				m_dDimensionLineStretch = Common.MMToFeet(2.5),
				m_dDimensionLineExtension = Common.MMToFeet(2.4),
				m_nColor = 65280,
				m_dTextSize = Common.MMToFeet(3.5),
				m_nTextBackground = 1,
				m_dTextOffset = 0.0,
				m_strTextFont = "仿宋"
			};
		}

		private Line GetDimensionLine(List<Element> lstGroups, Line line)
		{
			Element element = lstGroups.ElementAt(0);
			Element element2 = lstGroups.ElementAt(lstGroups.Count - 1);
			double instanceHeight = this.GetInstanceHeight(element);
			double num = (element.Location as LocationPoint).Point.Z + instanceHeight;
			double num2 = 0.0;
			if (typeof(Level) == element2.GetType())
			{
				num2 = (element2 as Level).Elevation;
			}
			else if (typeof(FamilyInstance) == element2.GetType())
			{
				if (null != line)
				{
					num2 = line.GetEndPoint(0).Z;
				}
				else
				{
					num2 = (element2.Location as LocationPoint).Point.Z;
				}
			}
			XYZ xyz = this.DimensionInsertPoint(lstGroups);
			XYZ xyz2 = new XYZ(xyz.X, xyz.Y, num);
			XYZ xyz3 = new XYZ(xyz.X, xyz.Y, num2);
			return Line.CreateBound(xyz2, xyz3);
		}

		public Document m_doc;

		public View m_view;

		private double m_dOffset = 1.6404199475065617;
	}
}
